package com.xishu.client;

import com.xishu.bo.printer.PrinterItem;
import com.xishu.bo.printer.PrinterLine;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.entity.hardware.Printer;
import com.xishu.entity.order.Order;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.VerifyUtil;
import com.xishu.service.CommonService;
import com.xishu.util.AES;
import com.xishu.util.CollectionUtil;
import com.xishu.util.Sha256Util;
import com.xishu.util.Tools;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.PRINTER_STATUS_ONLINE;

public class PrinterClient implements Tools {
    private static Logger logger = LoggerFactory.getLogger(PrinterClient.class);
    //在线
    public static final String ONLINE = "The online working condition is normal";
    //在线，但是没有纸张
    public static final String ONLINE_LOST_PAPER = "The online working state is abnormal";
    //离线
    public static final String OFFLINE = "off-line";

    private static PrinterClient instance = new PrinterClient();
    public static final String HK_URL = "http://api.jp.feieyun.com/Api/Open/";
    public static final String US_URL = "http://api.de.feieyun.com/Api/Open/";
    public static final int LINE_LENGTH = 48;

    public static String getApiUrl() {
        Config config = Config.getInstance();
        if (config.isSingleNode()) {
            return HK_URL;
        } else if (config.isHkVersion()) {
            return HK_URL;
        } else {
            return US_URL;
        }
    }

    private PrinterClient() {
    }

    public static PrinterClient getInstance() {
        return instance;
    }

    /**
     * 将订单转到成厨房的格式
     *
     * @param order
     * @return
     */
    public String orderToKitchen(Order order) {
        return "";
    }

    /**
     * 发送到厨房
     *
     * @param order
     */
    public void sendToKitchen(String printerSn, Order order) {
        printInfo(printerSn, "");
    }

    /**
     * 刷新单个打印机的状态
     *
     * @param printer
     */
    public void flushPrinterStatus(Printer printer) {
        String printerKey = AES.decrypt(printer.getPrinterKey());
        String printerSn = AES.decrypt(printer.getPrinterSn());

        if (isEmpty(printerKey) || isEmpty(printerSn)) {
            logger.info("printer {} do not config end", printer.getId());
            printer.setStatus(Constant.PRINTER_STATUS_OFFLINE);
            return;
        }

        String result = queryStatus(printerSn);

        try {
            logger.info("result is {}", result);
            if (isEmpty(result)) {
                logger.info("result is empty");
                return;
            }

            String statusData = JSONObject.fromObject(result).getString("data");
            if (statusData.toLowerCase().contains("condition")) {
                printer.setStatus(PRINTER_STATUS_ONLINE);
            } else if (statusData.toLowerCase().contains("online")) {
                printer.setStatus(Constant.PRINTER_STATUS_NO_PAPER);
            } else if (statusData.toLowerCase().contains("off-line")) {
                printer.setStatus(Constant.PRINTER_STATUS_OFFLINE);
            }

            //刷新数据库里面的状态
            CommonService.getInstance().directSave(printer);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 添加打印机，一次添加一台
     * SN 960803264
     * Key vpzy68wm
     *
     * @param printerKey
     * @param printerSn
     * @return
     */
    public String addPrinter(String printerSn, String printerKey, String remarks) {
        HashMap<String, String> map = new HashMap<>();
        map.put("printerContent", String.format("%s#%s#%s#", printerSn, printerKey, remarks));
        return sendCmd("Open_printerAddlist", map);
    }

    /**
     * 打印内容
     *
     * @return
     */
    public String printInfo(String printerSn, String content) {
        HashMap<String, String> map = new HashMap<>();
        map.put("sn", printerSn);
        map.put("content", content);
        return sendCmd("Open_printMsg", map);
    }

    /**
     * 查询打印机的状态
     *
     * @return
     */
    public String queryStatus(String printerSn) {
        HashMap<String, String> map = new HashMap<>();
        map.put("sn", printerSn);
        return sendCmd("Open_queryPrinterStatus", map);
    }

    /**
     * 签名
     *
     * @param stime
     * @return
     */
    public String sign(String stime) {
        String ukey = "CNM7wjaEysv37gwd";
        String user = "technology@dolatechnology.com";
        String input = user + ukey + stime;

        return Sha256Util.sha1(input);
    }

    /**
     * 发送命令
     *
     * @return
     */
    public String sendCmd(String cmd, Map<String, String> cmdParamMap) {
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        HashMap<String, String> paramsMap = new HashMap<>();
        String stime = String.valueOf(System.currentTimeMillis() / 1000);

        //公共参数
        paramsMap.put("user", "technology@dolatechnology.com");
        paramsMap.put("stime", stime);
        paramsMap.put("sig", sign(stime));
        paramsMap.put("apiname", cmd);
        //命令特胡的参数
        paramsMap.putAll(cmdParamMap);

        String result = post(getApiUrl(), headers, paramsMap);


        return result;
    }

    /**
     * 请求接口
     *
     * @param url
     * @param headers
     * @param paramsMap
     * @return
     */
    public static String post(String url, Map<String, String> headers, Map<String, String> paramsMap) {
        try {
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            //设置HTTP头
            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }

            //添加参数
            ArrayList<BasicNameValuePair> list = new ArrayList<>();
            paramsMap.forEach((key, value) -> list.add(new BasicNameValuePair(key, value)));

            httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));

            //执行
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity entity = httpResponse.getEntity();
            if (entity != null) {
                //状态，该状态可用于判断，此处略
                logger.info("resp_status:" + httpResponse.getStatusLine().getStatusCode());
                Charset respCharset = ContentType.getOrDefault(entity).getCharset();
                String respData = EntityUtils.toString(entity, respCharset);
                logger.info("respData is {}", respData);
                return respData;
            }
        } catch (IOException e) {
            logger.error("e", e);
        }

        return "";
    }

    /**
     * 一排有三个元素的时候，特殊处理
     *
     * @param item
     * @return
     */
    public String addStyleWith3Item(PrinterItem item) {
        logger.debug("addStyleWith3Item is {}", item);
        StringBuilder sb = new StringBuilder();
        sb.append(addSpace(item));
        return sb.toString();
    }

    /**
     * 添加空格
     *
     * @param printerItem
     * @return
     */
    public String addSpace(PrinterItem printerItem) {
        String str = printerItem.getContent();
        int size = printerItem.getLength();

        int len = 0;

        try {
            len = str.getBytes("GBK").length;
        } catch (UnsupportedEncodingException e) {
            logger.error("e", e);
        }


        //先添加前置空格
        if (printerItem.getPrefixLength() != 0) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < printerItem.getPrefixLength(); i++) {
                sb.append(" ");
            }

            logger.debug("add the prefix space size {} end", printerItem.getPrefixLength());
            str = sb.toString() + str;
        }

        //重新计算长度
        try {
            len = str.getBytes("GBK").length;
        } catch (UnsupportedEncodingException e) {
            logger.error("e", e);
        }

        //添加后置空格
        if (len < size) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < size - len; i++) {
                sb.append(" ");
            }

            logger.debug("add the afterfix space size is {} end", size - len);

            str = str + sb.toString();
        }
        return str;
    }

    /**
     * 在字符串后面添加空格
     *
     * @param str
     * @param length
     * @return
     */
    public String appendSpace(Object str, int length) {
        int gbkLength = getGbkLength(str);
        String generateSpace = generateSpace(length - gbkLength);
        return str + generateSpace;
    }

    /**
     * 添加分隔符
     *
     * @param str
     * @param length
     * @param separator
     * @return
     */
    public String appendSeparator(String str, int length, String separator) {
        int gbkLength = getGbkLength(str);
        String generateSeparator = generateSeparator(length - gbkLength, separator);
        return str + generateSeparator;
    }

    /**
     * 添加格式
     *
     * @param item
     * @return
     */
    public String addStyle(PrinterItem item) {
        logger.debug("add style item is {}", item);
        //logo直接返回
        if (getBoolean(item.getLogo())) {
            return "<LOGO>";
        }


        StringBuilder sb = new StringBuilder();

        if (isCenter(item)) {
            sb.append("<C>");
        }


        if (isCenterBold(item)) {
            sb.append("<CB>");
        }

        if (isDoubleShow(item)) {
            sb.append("<B>");
        }

        if (isBold(item)) {
            sb.append("<BOLD>");
        }

        if (isRight(item)) {
            sb.append("<RIGHT>");
        }

        if (getBoolean(item.getQrCode())) {
            sb.append("<QR>");
        }

        if (getBoolean(item.getxDouble())) {
            sb.append("<W>");
        }

        if (getBoolean(item.getyDouble())) {
            sb.append("<L>");
        }

        //居中对齐不添加空格
        //右对齐也不加空格
        if (isCenter(item) || isCenterBold(item) || isRight(item)) {
            sb.append(item.getContent());
        } else {
            if (getInt(item.getPrefixLength()) != 0) {
                //添加空格，再添加内容
                sb.append(addSpace(item));
            } else if (getBoolean(item.getDoubleShow())) {
                //需要加空格
                //字体本身变成了2倍，长度减少一半
                sb.append(PrintUtil4.addSpace(item.getContent(), (item.getLength()) / 2));
            } else {
                sb.append(PrintUtil4.addSpace(item.getContent(), item.getLength()));
            }
        }

        if (isCenter(item)) {
            sb.append("</C>");
        }

        if (isCenterBold(item)) {
            sb.append("</CB>");
        }

        if (isDoubleShow(item)) {
            sb.append("</B>");
        }

        if (isBold(item)) {
            sb.append("</BOLD>");
        }

        if (isRight(item)) {
            sb.append("</RIGHT>");
        }

        if (getBoolean(item.getQrCode())) {
            sb.append("</QR>");
        }

        if (getBoolean(item.getxDouble())) {
            sb.append("</W>");
        }

        if (getBoolean(item.getyDouble())) {
            sb.append("</L>");
        }

        return sb.toString();
    }

    /**
     * 判断内容是否有样式了
     *
     * @param content
     * @return
     */
    public boolean isContentContainStyle(String content) {
        if (content.toLowerCase().contains("<b>")) {
            return true;
        }

        if (content.toLowerCase().contains("<bold>")) {
            return true;
        }

        return false;
    }

    /**
     * 添加双倍
     *
     * @param content
     * @return
     */
    public String appendDouble(Object content) {
        return String.format("<B>%s</B>", content);
    }

    /**
     * 添加双倍的高度
     *
     * @param content
     * @return
     */
    public String appendHeight(Object content) {
        return String.format("<L>%s</L>", content);
    }

    /**
     * 加高，加粗
     *
     * @param content
     * @return
     */
    public String appendHeightAndBold(Object content) {
        return String.format("<L><BOLD>%s</BOLD></L>", content);
    }

    /**
     * 右对齐
     *
     * @param content
     * @return
     */
    public String appendRight(String content) {
        return String.format("<RIGHT>%s</RIGHT>", content);
    }

    /**
     * 添加变粗
     *
     * @param content
     * @return
     */
    public String appendBold(String content) {
        return String.format("<BOLD>%s</BOLD>", content);
    }

    /**
     * 产生空格,如果没有，那么就是产生“”
     *
     * @param size
     * @return
     */
    public String generateSpace(int size) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            stringBuilder.append(" ");
        }

        return stringBuilder.toString();
    }

    /**
     * 产生对应字符XX位
     *
     * @param size
     * @param character
     * @return
     */
    public String generateCh(int size, String character) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            stringBuilder.append(character);
        }

        return stringBuilder.toString();
    }

    /**
     * 添加指定位数的分隔符
     *
     * @param size
     * @param separator
     * @return
     */
    public String generateSeparator(int size, String separator) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            stringBuilder.append(separator);
        }

        return stringBuilder.toString();
    }

    /**
     * 打印一行
     *
     * @param printerLine
     */
    public String printerLine(PrinterLine printerLine) {
        List<PrinterItem> itemList = printerLine.getItemList();

        if (getBoolean(printerLine.getDirect())) {
            List<String> contentList = itemList.stream().map(p -> p.getContent()).collect(Collectors.toList());
            for (String s : contentList) {
                logger.debug("content{}end", s);
            }
            String joinLine = StringUtils.join(contentList, "");
            logger.debug("the join line is{}end", joinLine);
            return joinLine;
        }

        //只有一个元素
        if (itemList.size() == 1) {
            //一行元素直接打印，有居中则添加居中的标签
            PrinterItem item = itemList.get(0);
            logger.debug("item is {}", item);
            if (isEmpty(item.getContent())) {
                String style = addStyle(item);
                logger.debug("style is {} end", style);
                return style;
            }

            //只有一行，或者有多行，但是包含了样式了的多行，也不处理
            if (!item.getMaybeMultiLine()) {
                String style = addStyle(item);
                logger.debug("style is {} end", style);
                return style;
            } else {
                //先判断内容，如果超长了，那么用两行来
                List<String> contentList = getPrintContentList(item.getContent(), LINE_LENGTH);

                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < contentList.size(); i++) {
                    if (i == 0) {
                        item.setContent(contentList.get(i));
                        sb.append(addSpace(item));
                        sb.append("<BR>");
                    } else {
                        item.setPrefixLength(item.getNewLinePrefix());
                        item.setContent(contentList.get(i));
                        sb.append(addStyle(item));
                    }
                }

                String result = sb.toString();
                logger.debug("may multi line content is {} end", result);
                return result;
            }
        } else {
            //两列的情况,第二列要靠最右边，第一列需要用空格来占领
            if (itemList.size() == 2) {
                logger.debug("deal item list size 2");
                PrinterItem item1 = itemList.get(0);
                PrinterItem item2 = itemList.get(1);
                //没有设置长度的时候，自动处理长度
                if (item1.getLength() == 0) {
                    if (!getBoolean(item2.getDoubleShow())) {
                        item1.setLength(LINE_LENGTH - getGbkLength(item2.getContent()));
                    } else {
                        item1.setLength(LINE_LENGTH - getGbkLength(item2.getContent()) * 2);
                    }
                }
            } else if (itemList.size() == 3) {
                logger.debug("deal item list size 3");
                //计算每个元素的长度，特别是中间的元素的长度，有没有空格
                int oneItemLength = LINE_LENGTH / 3;
                PrinterItem item1 = itemList.get(0);
                PrinterItem item2 = itemList.get(1);
                PrinterItem item3 = itemList.get(2);

                //计算长度
                item1.setLength(oneItemLength);

                //中间的元素如果要居中，那么需要前置空格
                if (isCenter(item2)) {
                    int item2Length = getGbkLength(item2.getContent());
                    int prefixLength = (oneItemLength - item2Length) / 2;
                    item2.setLength(oneItemLength);
                    item2.setPrefixLength(prefixLength);
                }

                //第三个元素也要添加前置空格
                int item3Length = getGbkLength(item3.getContent());
                int prefixLength = oneItemLength - item3Length;
                item3.setPrefixLength(prefixLength);
            }

            //有多行，那么就有可能会换行
            //不换行的地方，直接获取值
            List<PrinterItem> notMultiContentItemList = itemList.stream().filter(p -> !getBoolean(p.getMaybeMultiLine())).collect(Collectors.toList());
            Optional<PrinterItem> multiLineItemOption = itemList.stream().filter(p -> getBoolean(p.getMaybeMultiLine())).findAny();

            //如果存在有可能有多行的元素，那么先拆分，第一行和其它行拼接，剩下的行单独打印，剩下的行单独时，但是也要拼接打印
            //有三个的，又有换行的，一定是菜品列表
            if (multiLineItemOption.isPresent()) {
                PrinterItem multiLineItem = multiLineItemOption.get();
                logger.debug("multiLineItem is {}", multiLineItem);
                //将可以索引字段拆成多段
                //找到需要打印多行的索引
                int index = itemList.indexOf(multiLineItem);
                List<String> contentList = getPrintContentList(multiLineItem.getContent(), multiLineItem.getLength());

                //只有一行，直接合成并打印
                if (contentList.size() == 1) {
                    logger.debug("only one line");
                    List<String> notMultiContentList = itemList.stream().map(item -> {
                        if (itemList.size() <= 2) {
                            return addStyle(item);
                        } else {
                            return addStyleWith3Item(item);
                        }
                    }).collect(Collectors.toList());
                    String join = String.join("", notMultiContentList.toArray(new String[notMultiContentList.size()]));
                    logger.debug("the print line is {} end", join);
                    return join;
                } else {
                    logger.debug("multi line");
                    //有多行，需要拆分处理
                    PrinterItem firstItem = multiLineItem.clone();
                    firstItem.setContent(contentList.get(0));
                    PrinterLine firstLine = new PrinterLine();

                    //先简单处理，如果出现了需要换行的在其它位置，再动态处理
                    firstLine.add(itemList.get(0));
                    firstLine.add(firstItem);

                    //厨房单没有价格，只有两列
                    //详单有价格，就会有三列
                    if (itemList.size() >= 3) {
                        firstLine.add(itemList.get(2));
                    }

                    String firstLineStr = printItemListToString(firstLine.getItemList());
                    StringBuilder sb = new StringBuilder();
                    logger.debug("the first line is {} end", firstLineStr);
                    //添加第一行的文本
                    sb.append(firstLineStr);
                    //添加换行
                    sb.append("<BR>");

                    //合并剩下的行
                    List<String> subContentList = contentList.subList(1, contentList.size());

                    List<String> subLineStrList = subContentList.stream().map(subString -> {
                        List<PrinterItem> subItemList = itemList.stream().map(p -> {
                            PrinterItem cloneItem = p.clone();
                            cloneItem.setContent("");
                            return cloneItem;
                        }).collect(Collectors.toList());

                        subItemList.get(1).setContent(subString);
                        return printItemListToString(subItemList);
                    }).collect(Collectors.toList());

                    sb.append(String.join("<BR>", subLineStrList));

                    String result = sb.toString();
                    logger.debug("the print line is {} end", result);
                    return result;
                }

            } else {
                //没有多行，那么直接拼接
                List<String> notMultiContentList = notMultiContentItemList.stream().map(item -> {
                    if (itemList.size() <= 2) {
                        return addStyle(item);
                    } else {
                        return addStyleWith3Item(item);
                    }
                }).collect(Collectors.toList());
                String join = String.join("", notMultiContentList.toArray(new String[notMultiContentList.size()]));
                logger.debug("the print line is {}", join);
                return join;
            }
        }

    }

    /**
     * 打印元素转换成字符串
     *
     * @param itemList
     * @return
     */
    public String printItemListToString(List<PrinterItem> itemList) {
        List<String> notMultiContentList = itemList.stream().map(item -> {
            if (itemList.size() <= 2) {
                return addStyle(item);
            } else {
                return addStyleWith3Item(item);
            }
        }).collect(Collectors.toList());
        return String.join("", notMultiContentList.toArray(new String[notMultiContentList.size()]));
    }

    /**
     * 这一行添加空格
     *
     * @param str
     * @param width
     * @return
     */
    public String titleAddSpace(String str, int width) {
        int k = 0;
        try {
            k = str.getBytes("GBK").length;
        } catch (UnsupportedEncodingException e) {
            logger.error("e", e);
        }
        for (int i = 0; i < width - k; i++) {
            str += " ";
        }
        return str;
    }

    /**
     * 获取实际长度
     *
     * @param str
     * @return
     */
    public int getGbkLength(Object str) {
        try {
            return str.toString().getBytes("GBK").length;
        } catch (UnsupportedEncodingException e) {
            logger.error("e", e);
        }

        return 0;
    }

    /**
     * 获取字符串长度
     *
     * @param content
     * @param length
     * @return
     */
    public List<String> getStringList(String content, int length, Boolean zh) {
        if (zh == null || getBoolean(zh)) {
            ArrayList<String> result = new ArrayList<>();

            StringBuilder sb = new StringBuilder();
            //找到满足等于长度就下一个
            for (int i = 0; i < content.length(); i++) {
                sb.append(content.charAt(i));
                if (getGbkLength(sb.toString()) >= length - 1) {
                    result.add(sb.toString());
                    sb = new StringBuilder();
                }
            }

            if (isNotEmpty(sb.toString())) {
                result.add(sb.toString());
            }

            return result;
        } else {
            //英文算法
            List<String> result = new ArrayList<>();

            String[] split = content.split("\\s+");
            StringBuilder sb = new StringBuilder();

            for (String s : split) {
                //如果这次添加了将超过长度，那么添加到下一行
                if (getGbkLength(sb.toString() + s) >= length - 1) {
                    result.add(sb.toString());
                    sb = new StringBuilder();
                }

                //已经添加過了，那么添加了空格
                if (isNotEmpty(sb.toString())) {
                    sb.append(" ");
                }

                sb.append(s);
            }

            if (isNotEmpty(sb.toString())) {
                result.add(sb.toString());
            }

            return result;
        }
    }

    /**
     * 一个元素分行列表,只有两行
     * 第一行没有换行符，剩下的行都有换行符
     * 这里会加上打印的含义
     *
     * @param content
     * @param size
     * @return
     */
    public List<String> getPrintContentList(String content, int size) {
        logger.debug("get content list  {} begin", content);
        List<String> resultList = new ArrayList<>();

        //这一行超长了，需要占用多行
        logger.debug("size is {}", size);
        List<String> list = PrintUtil4.getStrList(content, size);
        //第一行头
        String s0 = titleAddSpace(list.get(0), size);
        resultList.add(s0);

        String s = "";
        //添加单独的每一行
        for (int k = 1; k < list.size(); k++) {
            s += list.get(k);
        }

        try {
            if (list.size() > 1) {
                //这里会加上打印的含义
                s = PrintUtil4.getStringByEnter(size, s);
                resultList.add(s);
            }

        } catch (Exception e) {
            logger.error("e", e);
        }

        logger.debug("content list is {}", StringUtils.join(resultList, ":"));
        return resultList;
    }

    /**
     * 是否是居中，但是不加粗
     *
     * @param item
     * @return
     */
    public boolean isCenter(PrinterItem item) {
        return getBoolean(item.getCenter()) && !getBoolean(item.getBold());
    }

    /**
     * 是否是居中，但是加粗
     *
     * @param item
     * @return
     */
    public boolean isCenterBold(PrinterItem item) {
        return getBoolean(item.getCenter()) && getBoolean(item.getBold());
    }

    /**
     * 只变粗
     *
     * @param item
     * @return
     */
    public boolean isBold(PrinterItem item) {
        return !getBoolean(item.getCenter()) && getBoolean(item.getBold());
    }

    /**
     * 两倍显示
     *
     * @param item
     * @return
     */
    public boolean isDoubleShow(PrinterItem item) {
        return getBoolean(item.getDoubleShow());
    }

    /**
     * 是否靠右
     *
     * @param item
     * @return
     */
    public boolean isRight(PrinterItem item) {
        return getBoolean(item.getRight());
    }


    /**
     * 打印整个订单
     *
     * @param printerLineList
     */
    public String printContent(List<PrinterLine> printerLineList) {
        StringBuilder printerContent = new StringBuilder();

        //拼接每一行
        printerLineList.forEach(line -> {
            printerContent.append(printerLine(line));
            if (line.isNewLine()) {
                printerContent.append("<BR>");
            }
        });

        return printerContent.toString();
    }

    /**
     * 一行的分隔符
     *
     * @return
     */
    public PrinterLine printSeparator() {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < LINE_LENGTH; i++) {
            buffer.append("-");
        }

        printerItem.setContent(buffer.toString());
        printerLine.add(printerItem);

        return printerLine;
    }

    /**
     * 两行的分隔符
     *
     * @return
     */
    public PrinterLine printTwoLineSeparator() {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < LINE_LENGTH; i++) {
            buffer.append("=");
        }

        printerItem.setContent(buffer.toString());
        printerLine.add(printerItem);

        return printerLine;
    }

    /**
     * 一行的 ******************
     *
     * @return
     */
    public PrinterLine printLineAsterisk() {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < LINE_LENGTH; i++) {
            buffer.append("*");
        }

        printerItem.setContent(buffer.toString());
        printerLine.add(printerItem);

        return printerLine;
    }


    /**
     * 新的一行，只有一个内容
     *
     * @param content
     * @return
     */
    public PrinterLine newLine(String content, Boolean center, Boolean bold) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        printerItem.setContent(content);
        printerItem.setCenter(center);
        printerItem.setBold(bold);
        return printerLine;
    }

    public PrinterLine newLine(String content, Boolean doubleShow) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        printerItem.setContent(content);
        printerItem.setDoubleShow(doubleShow);
        return printerLine;
    }


    public PrinterLine newLine() {
        PrinterLine printerLine = new PrinterLine();
        printerLine.setDirect(true);
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        return printerLine;
    }

    public PrinterLine newLine(String content) {
        PrinterLine printerLine = new PrinterLine();
        printerLine.setDirect(true);
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        printerItem.setContent(content);
        return printerLine;
    }

    /**
     * 产生一行数据
     *
     * @return
     */
    public PrinterLine directLine() {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        printerLine.setDirect(true);
        return printerLine;
    }

    /**
     * 产生一行，放在右边
     *
     * @param content
     * @param bold
     * @return
     */
    public PrinterLine newRightLine(String content, Boolean bold) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerLine.add(printerItem);
        printerItem.setContent(content);
        printerItem.setRight(true);
        printerItem.setBold(bold);
        return printerLine;
    }

    /**
     * 一行只有两条数据，第一条是标签，第二条是真实数据
     *
     * @param labelItemContent
     * @param rightItemContent
     * @return
     */
    public PrinterLine newTwoItemLine(String labelItemContent, String rightItemContent, Boolean bold, Boolean doubleShow) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem labelItem = new PrinterItem();
        labelItem.setContent(labelItemContent);
        labelItem.setBold(bold);
        labelItem.setDoubleShow(doubleShow);
        printerLine.add(labelItem);

        PrinterItem dataItem = new PrinterItem();
        dataItem.setContent(rightItemContent);
        dataItem.setBold(bold);
        dataItem.setDoubleShow(doubleShow);
        printerLine.add(dataItem);

        return printerLine;
    }

    /**
     * 产生三个元素的一行
     *
     * @param firstItemContent
     * @param middleItemContent
     * @param lastItemContent
     * @return
     */
    public PrinterLine newThreeItemLine(String firstItemContent, String middleItemContent, String lastItemContent, Boolean bold) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem firstItem = new PrinterItem();
        firstItem.setContent(firstItemContent);
        firstItem.setBold(bold);
        printerLine.add(firstItem);

        PrinterItem middleItem = new PrinterItem();
        middleItem.setContent(middleItemContent);
        middleItem.setBold(bold);
        middleItem.setCenter(true);
        printerLine.add(middleItem);

        PrinterItem lastItem = new PrinterItem();
        lastItem.setContent(lastItemContent);
        lastItem.setBold(bold);
        printerLine.add(lastItem);

        return printerLine;
    }

    /**
     * 空行
     *
     * @return
     */
    public PrinterLine emptyLine() {
        return new PrinterLine();
    }

    /**
     * 是否是空行
     *
     * @param printerLine
     * @return
     */
    public boolean isEmptyLine(PrinterLine printerLine) throws ResponseStatusException {
        VerifyUtil.verify(() -> printerLine != null);
        if (isEmpty(printerLine.getItemList())) {
            return true;
        }

        List<PrinterItem> itemList = printerLine.getItemList();
        PrinterItem printerItem = itemList.get(0);
        return isEmpty(printerItem.getContent());
    }

    public void removeLastEmptyLine(List<PrinterLine> printerLineList) throws ResponseStatusException {
        if (isEmpty(printerLineList)) {
            return;
        }

        PrinterLine lastLine = CollectionUtil.getInstance().last(printerLineList);
        if (!isEmptyLine(lastLine)) {
            logger.info("not empty line");
            return;
        }

        printerLineList.remove(lastLine);
        return;
    }

    public void removeFirstEmptyLine(List<PrinterLine> printerLineList) throws ResponseStatusException {
        if (isEmpty(printerLineList)) {
            return;
        }
        PrinterLine firstLine = CollectionUtil.getInstance().first(printerLineList);
        if (!isEmptyLine(firstLine)) {
            logger.info("not empty line");
            return;
        }
        printerLineList.remove(firstLine);
        return;

    }

    /**
     * 添加LOGO
     *
     * @return
     */
    public PrinterLine newLogo() {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerItem.setLogo(true);
        printerLine.add(printerItem);
        return printerLine;
    }

    /**
     * 获取条形码
     *
     * @param code
     * @return
     */
    public PrinterLine newBarCode(String code) {
        //小于10位的，添加0
        code = append(code, 10);
        String digitBarCode = PrinterBarCode.getDigitBarCode(code);
        return newLine(digitBarCode, true, null);
    }

    /**
     * 产生二维码
     *
     * @param qrCode
     * @return
     */
    public PrinterLine newQrCode(String qrCode) {
        PrinterLine printerLine = new PrinterLine();
        PrinterItem printerItem = new PrinterItem();
        printerItem.setQrCode(true);
        printerItem.setContent(qrCode);

        printerLine.add(printerItem);
        return printerLine;
    }

    /**
     * 变大，变粗
     *
     * @param str
     * @return
     */
    public String appendDoubleShowAndBold(String str) {
        StringBuilder builder = new StringBuilder();
        builder.append("<B>");
        builder.append("<BOLD>");
        builder.append(str);
        builder.append("</BOLD>");
        builder.append("</B>");
        return builder.toString();
    }

    /**
     * 添加居中
     *
     * <C>居中标签自动占一行的，加多BR就会大点间距，删除就会小点
     *
     * @param str
     * @return
     */
    public String appendCenter(String str) {
        return String.format("<C>%s</C>", str);
    }

    /**
     * 字占中间，前后端需要占空格
     *
     * @param str
     * @param length
     * @return
     */
    public String centerLength(Object str, int length) {
        int gbkLength = getGbkLength(str.toString());
        int appendLength = (length - gbkLength) / 2;
        String spaceList = generateSpace(appendLength);

        //刚好均分了
        if (gbkLength + appendLength * 2 == length) {
            return spaceList + str + spaceList;
        }

        //没有均分，那么最后一个补上
        return spaceList + str + generateSpace(appendLength + 1);
    }


    /**
     * 只变大一倍
     *
     * @param str
     * @return
     */
    public String appendDoubleShow(String str) {
        StringBuilder builder = new StringBuilder();
        builder.append("<B>");
        builder.append(str);
        builder.append("</B>");
        return builder.toString();
    }

    /**
     * 位数不够，前面添加0
     *
     * @param str
     * @param length
     * @return
     */
    public String append(String str, int length) {
        if (str.length() < length) {
            StringBuilder builder = new StringBuilder();

            for (int i = 0; i < length - str.length(); i++) {
                builder.append("0");
            }

            builder.append(str);
            return builder.toString();
        } else {
            return str;
        }
    }

    /**
     * 打印
     * <p>
     * {
     * "msg":"ok",
     * "ret":0,
     * "data":"816501678_20160919184316_1419533539",
     * "serverExecutedTime":3
     * }
     * <p>
     * {
     * "msg":"参数错误 : 该帐号未注册.",
     * "ret":-2,
     * "data":null,
     * "serverExecutedTime":37
     * }
     *
     * @param printerLineList
     * @param sn
     */
    public boolean printList(List<PrinterLine> printerLineList, String sn, Long printerId) throws Exception {
        if (!Config.getInstance().canPrint()) {
            logger.info("close print the printer");
            return false;
        }

        for (List<PrinterLine> printerLines : separatePrintLine(printerLineList)) {
            String content = printContent(printerLines);
            logger.info("content is {}", content);
            String printInfo = printInfo(sn, content);
            JSONObject resultJson = JSONObject.fromObject(printInfo);
            int status = resultJson.getInt("ret");
            //打印任务先全部都是成功处理，后面添加打印任务，由餐厅自行决定要不要打印。
        }

        return true;
    }

    /**
     * 将打印分行，如果超过了4000字节，则另起一行
     *
     * @param printerLineList
     * @return
     */
    public List<List<PrinterLine>> separatePrintLine(List<PrinterLine> printerLineList) {
        List<List> partitionList = CollectionUtil.getInstance().partition(printerLineList, 10);
        StringBuilder sb = new StringBuilder();
        int partitionSize = partitionList.size();

        List<List<PrinterLine>> resultList = new ArrayList<>();
        int index = 0;
        List<PrinterLine> oneGroupPrinterList = new ArrayList<>();

        for (List list : partitionList) {
            index++;

            sb.append(printContent(list));

            if (sb.toString().getBytes().length >= Config.getInstance().printerMaxByteSize()) {
                logger.info("add the one group print line");
                oneGroupPrinterList.add(newLine("To be continued..", true, true));
                resultList.add(oneGroupPrinterList);

                oneGroupPrinterList = new ArrayList<>();
                oneGroupPrinterList.add(newLine("Continued...", true, true));
                oneGroupPrinterList.addAll(list);
                sb = new StringBuilder();
                sb.append(printContent(oneGroupPrinterList));
            } else {
                oneGroupPrinterList.addAll(list);
            }

            //最后一个了，直接加
            if (index == partitionSize) {
                if (isNotEmpty(oneGroupPrinterList)) {
                    resultList.add(oneGroupPrinterList);
                }
            }
        }

        return resultList;
    }

    /**
     * 打印结果是否成功
     *
     * @param printInfoResult
     * @return
     */
    public boolean isPrinterOk(String printInfoResult) {
        JSONObject resultJson = JSONObject.fromObject(printInfoResult);
        int status = resultJson.getInt("ret");
        return status == 0;
    }

    /**
     * function reverse($str)
     * {
     * $b=array();
     * $b[0] = "\x1d";
     * $b[1] = "\x42";
     * $b[2] = "1";//开启黑白反显打印模式
     * $start = implode("",$b);
     * $b2=array();
     * $b2[0] = "\x1d";
     * $b2[1] = "\x42";
     * $b2[2] = "0";//关闭黑白反显打印模式
     * $end = implode("",$b2);
     * return $start.$str.$end;
     * }
     * $orderInfo = reverse('黑白反显打印测试');
     * printMsg(SN,$orderInfo,1);
     * <p>
     * <p>
     * 开启黑白反显打印模式
     *
     * @return
     */
    public String openBlackGround() {
        return "\\x1d\\x421";
    }

    /**
     * 关闭黑白反显打印模式
     *
     * @return
     */
    public String closeBlackGround() {
        return "\\x1d\\x420";
    }

    public Boolean isEn(String str) {
        return PrintUtil4.isEn(str);
    }

}
